package com.jms.web.mvc.context;

import com.jms.framework.beans.BeanDefinition;
import com.jms.framework.beans.exception.BeansException;
import com.jms.framework.config.JmsConfigItem;
import com.jms.framework.context.ApplicationContext;
import com.jms.framework.exception.PropertyAlreadySetException;
import com.jms.framework.mvc.context.MvcContext;
import com.jms.web.mvc.annotaion.Controller;
import com.jms.web.mvc.annotaion.RequestMapping;
import com.jms.web.mvc.creator.ArgumentCreator;
import com.jms.web.mvc.handler.MethodHandler;
import com.jms.web.mvc.interceptor.HandlerInterceptor;
import com.jms.web.mvc.resolver.HandlerMethodArgumentResolver;
import com.jms.web.mvc.resource.StaticResourceManagement;
import com.jms.web.mvc.servlet.DispatcherServlet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * WebMvcContext 是一个实现了 MvcContext 接口的单例类，用于管理 MVC 上下文信息。
 * 它负责初始化控制器方法与 URI 的映射关系，并提供对静态资源路径、应用上下文等信息的访问。
 */
public class WebMvcContext implements MvcContext {
    private static final Logger logger = LogManager.getLogger(WebMvcContext.class);
    private volatile static WebMvcContext instance;
    private ApplicationContext applicationContext;
    private LinkedList<MethodHandler> methodHandlers;
    private LinkedList<HandlerInterceptor> interceptors;
    private String[] staticResourcePath;
    private long staticResourceCacheTime;
    List<ArgumentCreator> argumentCreators;

    /**
     * 私有构造函数，防止外部实例化。
     */
    private WebMvcContext() {

    }

    /**
     * 获取 WebMvcContext 的单例实例。
     *
     * @return WebMvcContext 实例
     */
    public static WebMvcContext getInstance() {
        if (instance == null) {
            synchronized (WebMvcContext.class) {
                if (instance == null) {
                    instance = new WebMvcContext();
                }
            }
        }
        return instance;
    }

    /**
     * 设置应用程序上下文。
     *
     * @param applicationContext 应用程序上下文对象
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        if (this.applicationContext != null) {
            throw new PropertyAlreadySetException(ApplicationContext.class, "applicationContext");
        }
        this.applicationContext = applicationContext;
    }

    /**
     * 获取当前的应用程序上下文。
     *
     * @return ApplicationContext 应用程序上下文对象
     */
    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取所有 URI 映射处理器列表。
     *
     * @return LinkedList<MethodHandler> 方法处理器链表
     */
    public LinkedList<MethodHandler> getMethodHandlers() {
        return methodHandlers;
    }

    /**
     * 获取配置的静态资源路径数组。
     *
     * @return String[] 静态资源路径数组
     */
    public String[] getStaticResourcePath() {
        return staticResourcePath;
    }

    public LinkedList<HandlerInterceptor> getInterceptors() {
        return interceptors;
    }

    public long getStaticResourceCacheTime() {
        return staticResourceCacheTime;
    }

    public List<ArgumentCreator> getArgumentCreators() {
        return argumentCreators;
    }

    /**
     * 初始化 MVC 上下文环境。
     * 包括加载静态资源路径以及扫描所有 Controller 类中的 RequestMapping 注解，
     * 构建 URI 到方法处理器的映射关系。
     * 同时注册拦截器并设置 DispatcherServlet 的上下文。
     */
    @Override
    public void init() {
        logger.info("Mvc context starts initializing.");

        // 初始化静态资源属性：获取配置的静态资源路径与缓存时间
        String staticResourcePaths = applicationContext.getJmsConfig().getAttribute(JmsConfigItem.JMS_MVC_RESOURCE_PATH.getValue());
        staticResourcePath = staticResourcePaths != null ? staticResourcePaths.split(",") : null;
        String time = applicationContext.getJmsConfig().getAttribute(JmsConfigItem.JMS_MVC_RESOURCE_CACHE_TIME.getValue());
        staticResourceCacheTime = time != null ? Long.parseLong(time) : 60 * 1000 * 60;
        StaticResourceManagement.setWebMvcContext(this);

        // 初始化URL映射以及拦截器列表
        methodHandlers = new LinkedList<>();
        interceptors = new LinkedList<>();
        argumentCreators = new LinkedList<>();

        // 扫描所有Bean定义，查找Controller类和RequestMapping注解以构建URI映射
        BeanDefinition[] beanDefinitions = applicationContext.getBeanContext().getBeanDefinitions();
        for (BeanDefinition beanDefinition: beanDefinitions) {
            Class<?> beanClass;
            try {
                beanClass = Class.forName(beanDefinition.getBeanClassName());
            } catch (ClassNotFoundException e) {
                throw new BeansException("not found the bean class:" + beanDefinition.getBeanClassName(), e);
            }
            // 处理带有Controller注解的类
            if (beanClass.isAnnotationPresent(Controller.class)) {
                Method[] methods = beanClass.getMethods();
                for (Method method : methods) {
                    // 处理带有RequestMapping注解的方法
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        MethodHandler methodHandler = new MethodHandler(method.getName(), beanDefinition.getBeanName(), requestMapping.method(), requestMapping.value(), method.getParameters());
                        methodHandlers.add(methodHandler);
                    }
                }
            }

            // 处理实现了HandlerInterceptor接口的拦截器Bean
            if (HandlerInterceptor.class.isAssignableFrom(beanClass)) {
                HandlerInterceptor interceptor = (HandlerInterceptor) applicationContext.getBean(beanDefinition.getBeanName());
                interceptors.add(interceptor);
            }

            if (ArgumentCreator.class.isAssignableFrom(beanClass)) {
                ArgumentCreator argumentCreator = (ArgumentCreator) applicationContext.getBean(beanDefinition.getBeanName());
                argumentCreators.add(argumentCreator);
            }
        }

        argumentCreators.sort(ArgumentCreator::compareTo);
        HandlerMethodArgumentResolver.setWebMvcContext(this);

        // 设置DispatcherServlet的WebMvc上下文
        DispatcherServlet dispatcherServlet = DispatcherServlet.getInstance();
        dispatcherServlet.setWebMvcContext(this);

        logger.info("Mvc context initialization is complete.");
    }


    /**
     * 销毁当前上下文环境，释放资源。
     */
    @Override
    public void destroy() {
        logger.info("Mvc context starts destroying.");

        applicationContext = null;
        if (interceptors != null) {
            interceptors.clear();
            interceptors = null;
        }
        if (argumentCreators != null) {
            argumentCreators.clear();
            argumentCreators = null;
        }
        if (methodHandlers != null) {
            methodHandlers.clear();
            methodHandlers = null;
        }
        staticResourcePath = null;

        StaticResourceManagement.flushCache();

        logger.info("Mvc context destruction is complete.");
    }
}
